﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Web;

namespace OAuthServer.Controllers;

public class OAuthController : Controller
{
    private static readonly RSA key = RSA.Create();
    public IDataProtectionProvider _provider;

    public OAuthController(IDataProtectionProvider provider)
    {
        _provider = provider;
    }

    [Authorize("authed")]
    public bool authorize()
    {
        var result =HttpUtility.ParseQueryString(Request.QueryString.ToString());

        string client_id = "", code_challenge = "", code_challenge_method = "", redirect_uri = "", state = "", code_verifier="";

        foreach(var key in result.AllKeys)
        {
            if (key == "client_id") client_id = result[key];
            
            else if (key == "code_verifier") code_verifier = result[key];
            else if (key == "code_challenge") code_challenge = result[key];
            else if (key == "code_challenge_method") code_challenge_method = result[key];
            else if (key == "redirect_uri") redirect_uri = result[key];
            else if (key == "state") state = result[key];
        }

        var protector =_provider.CreateProtector("oauth");

        var code = new AuthCode()
        {
            ClientId = client_id,
            CodeChallenge = code_challenge,
            CodeChallengeMethod = code_challenge_method,
            RedirectUri = redirect_uri,
            Expiry = DateTime.Now.AddMinutes(5)
        };
        var codeStr =protector.Protect(JsonSerializer.Serialize(code));
        Response.Redirect($"{redirect_uri}?code={codeStr}&state={state}&iss=liya");

        return true;
    }

    public async Task<string> jwks() {

            //var jwk = new JsonWebKey
            //{
            //    Kty = "RSA",
            //    Alg = "RS256",
            //    Use = "sig", // 或者"enc"取决于你的用途
            //    Kid = Guid.NewGuid().ToString(), // 通常基于实际应用生成唯一标识符
            //    E = Base64UrlEncoder.Encode(key.ExportParameters(false).Exponent),
            //    N = Base64UrlEncoder.Encode(key.ExportParameters(false).Modulus),
            //};
 
string json = @"
{
  ""keys"": [
    {
      ""kty"": ""RSA"",
      ""use"": ""sig"",
      ""kid"": ""your-key-id"",
      ""alg"": ""RS256"",
      ""n"": ""base64url-encoded-n-modulus"",
      ""e"": ""base64url-encoded-e-exponent""
    }
  ]
}";
        // 解析 JSON 字符串
        JObject jsonObject = JObject.Parse(json);
        jsonObject["keys"][0]["kid"] = Guid.NewGuid().ToString();
        jsonObject["keys"][0]["e"] = Base64UrlEncoder.Encode(key.ExportParameters(false).Exponent);
        jsonObject["keys"][0]["n"] = Base64UrlEncoder.Encode(key.ExportParameters(false).Modulus);
        // 将修改后的对象转换回 JSON 字符串
        string updatedJson = jsonObject.ToString();


        return updatedJson;
       
    }
    public async Task<AuthToken> token()
    {
        var body = await Request.BodyReader.ReadAsync();
        var content = Encoding.UTF8.GetString(body.Buffer);

        string grantType = "", code = "", redirectUri = "", codeVerify = "";
        foreach (var part in content.Split('&'))
        {
            var subParts = part.Split('=');
            var key = subParts[0];
            var value = subParts[1];
            if (key == "grant_type") grantType = value;
            else if (key == "code") code = value;
            else if (key == "redirect_uri") redirectUri = value;
            else if (key == "code_verifier") codeVerify = value;
        }
        var protect = _provider.CreateProtector("oauth");
        var codestr = protect.Unprotect(code);
        var authCode = JsonSerializer.Deserialize<AuthCode>(codestr);

        if (!ValidateCode(authCode, codeVerify))
        {
            return null;
        }
        AuthToken authToken = new AuthToken();
        authToken.expires_in = DateTime.Now.AddSeconds(120).ToString();
        authToken.id_token = IssueToken();
        authToken.access_token = IssueToken();
        authToken.refresh_token = IssueToken(60 * 60 * 24);
        authToken.token_type = "Bearer";



        return authToken;

    }

    public async Task<AuthToken> RefreshToken(AuthToken authToken)
    {

        var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
        bool isCan = jwtSecurityTokenHandler.CanReadToken(authToken.refresh_token);

        if (!isCan)
        {
            return new();
        }
        var validationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = false,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(1),

            ValidAudience = "liya00",
            //ValidAudience = "OAuth",
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new RsaSecurityKey(key)

        };
        SecurityToken securityToken = null;
        try
        {
            jwtSecurityTokenHandler.ValidateToken(authToken.refresh_token, validationParameters, out securityToken);
        }
        catch (Exception ex)
        {
            return new();
        }

        //authToken.id_token = Guid.NewGuid().ToString();

        authToken.expires_in = DateTime.Now.AddSeconds(120).ToString();

        authToken.access_token = IssueToken();
        authToken.refresh_token = IssueToken(60 * 60 * 24);
        authToken.token_type = "Bearer";



        return authToken;
    }

    public string IssueToken(double seconds = 120)
    {
        // JWT
        var sercet = new RsaSecurityKey(key);

        var jwt = new JsonWebTokenHandler().CreateToken(new SecurityTokenDescriptor()
        {
            Issuer="sssss",
            Audience = "OAuth",
            Subject = new ClaimsIdentity(new List<Claim>
            {
                new Claim("sub","liya"),
                new Claim("name","liya"),
                new Claim("custom","foo")
            }),
            Expires = DateTime.Now.AddSeconds(seconds),
            TokenType = "Bearer",
            SigningCredentials = new SigningCredentials(sercet, SecurityAlgorithms.RsaSha256)
        });
        return jwt;
    }


    public static bool ValidateCode(AuthCode code,string codeVerfy)
    {
        var sha256 = SHA256.Create();
        var codeVery=Base64UrlEncoder.Encode( sha256.ComputeHash(Encoding.ASCII.GetBytes(codeVerfy)));
        return codeVery.Equals(code.CodeChallenge);
    }

    public async Task<IActionResult> Info()
    {
        //if(HttpContext.User.Identity.IsAuthenticated==false)
        //    return Unauthorized("未登录...");

        string content =Request.Headers.Authorization;
        string access_token = content.Split(" ")[1];

        var jwtSecurityTokenHandler=new JwtSecurityTokenHandler();
        bool isCan = jwtSecurityTokenHandler.CanReadToken(access_token);

        if (!isCan)
        {
            return Unauthorized("验证token的格式无效");
        }
        var validationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = false,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(1),

            ValidateIssuerSigningKey = true,
            ValidAudience = "liya00",
            IssuerSigningKey = new RsaSecurityKey(key)

        };
        SecurityToken securityToken = null;
        try
        {
            jwtSecurityTokenHandler.ValidateToken(access_token, validationParameters, out securityToken);
        }catch (Exception ex)
        {
            return Unauthorized("验证失败");
        }

        var payload = access_token?.Split('.')[1];
        if (!string.IsNullOrEmpty(payload))
        {
            var pl=Base64UrlEncoder.Decode(payload);
            var data = JsonDocument.Parse(pl);
            return Ok(JsonSerializer.Serialize(data));
        }
        return Unauthorized("fail");

    }
}
public class AuthToken
{
    public string id_token { get; set; }
    public string access_token { get; set; }

    public string refresh_token { get; set; }

    public string token_type { get; set; }

    public string expires_in { get; set; }
}
public class AuthCode
{
    public string ClientId { get; set; }
    public string CodeChallenge { get; set; }
    public string CodeChallengeMethod { get; set; }
    public string RedirectUri { get; set; }

    public DateTime Expiry { get; set; }
}
